package com.qiandw.mailSendService.module;

import com.itextpdf.io.image.ImageData;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.text.BadElementException;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.io.RASInputStream;
import com.itextpdf.text.io.RandomAccessSourceFactory;
import com.itextpdf.text.pdf.*;
import com.itextpdf.text.pdf.security.*;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import java.io.*;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.List;

public class PdfSignUtil {


    public static void signPDF(String pdfPath, String pdfOutPath, String moulagePath, String certPath, String password){

        try
        {
            byte [] certData = FileUtil.localBufferRead(certPath); //证书流

            KeyStore ks = CertUtil.getKeyStoreByPfx(certData, password);
            Certificate[] certChain = CertUtil.getCertificateChainByKeyStore(ks);
            PrivateKey privateKey = CertUtil.getPrivateKeyByKeyStore(ks, password);

            //读取待签名pdf
            byte[] fileData = FileUtil.localBufferRead(pdfPath);

            ByteArrayInputStream fbis = new ByteArrayInputStream(fileData); //输入流
            PdfReader reader = new PdfReader(fbis); //阅读者


            ByteArrayOutputStream faos = new ByteArrayOutputStream(); //输出流

            //create signature private
            PdfStamper stamper = PdfStamper.createSignature(reader, faos, '0', null, true); //创建当前位置信息签名
            PdfSignatureAppearance appearance = stamper.getSignatureAppearance(); //签名附加信息
            appearance.setReason("这里设置签名的原因");
            appearance.setLocation("这里设置签名的地址");

            FileInputStream imgIns = new FileInputStream(moulagePath);
            java.nio.ByteBuffer byteBuffer = java.nio.ByteBuffer.allocate(imgIns.available());
            imgIns.getChannel().read(byteBuffer);
            imgIns.close();
            PdfSignUtil.doImageStamp(stamper,byteBuffer.array(), 160, reader.getPageSize(1).getHeight() - 120, 260, reader.getPageSize(1).getHeight() - 20, 1);
            stamper.getWriter().setCompressionLevel(5);
            // 摘要算法
            ExternalDigest digest = new BouncyCastleDigest();
            BouncyCastleProvider provider = new BouncyCastleProvider();
            Security.addProvider(provider);
            ExternalSignature externalSignature = new PrivateKeySignature(privateKey, DigestAlgorithms.SHA1, provider.getName()); //签名过程 -- 签名提供者
            //ExternalSignature externalSignature = new PrivateKeySignature(privateKey, DigestAlgorithms.SHA1, null);
            MakeSignature.signDetached(appearance, digest, externalSignature, certChain, null, null, null, 0, MakeSignature.CryptoStandard.CMS);
            FileUtil.localBufferWrite(faos.toByteArray(), pdfOutPath); //输出流
            reader.close();
            faos.close();
            fileData.clone();
            fbis.close();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    //签章
    public static void doImageStamp(PdfStamper pdfStamper, byte[] imgBytes, float leftBottomX, float leftBottomY, float imgWidth, float imgHeight, int pageNum){
        try {
            ImageData imageData = ImageDataFactory.create(imgBytes);
            PdfSignatureAppearance appearance = pdfStamper.getSignatureAppearance();
            //四个参数的分别是，图章左下角x，图章左下角y，图章右上角x，图章右上角y
            Rectangle rectangle = new Rectangle(leftBottomX, leftBottomY, imgWidth, imgHeight);
            appearance.setVisibleSignature(rectangle, pageNum, "sig");
            appearance.setRenderingMode(PdfSignatureAppearance.RenderingMode.GRAPHIC);
            appearance.setSignatureGraphic(Image.getInstance(imgBytes));
            //没有被签章的
            appearance.setCertificationLevel(PdfSignatureAppearance.NOT_CERTIFIED);
        } catch (BadElementException | IOException e) {
            e.printStackTrace();
        }
    }

    //获取签名信息
    public static byte[] getDigitalSignature(PdfReader pdfReader, String signedName){
        PdfDictionary pdfDictionary = pdfReader.getAcroFields().getSignatureDictionary(signedName);
        PdfString contexts = pdfDictionary.getAsString(PdfName.CONTENTS);
        return contexts.getBytes();
    }

    //获取元数据
    public static byte[] getOriginData(PdfReader pdfReader, String signedName){
        byte[] originDate = null;
        try {
            PdfDictionary pdfDictionary = pdfReader.getAcroFields().getSignatureDictionary(signedName);
            PdfArray pdfArray = pdfDictionary.getAsArray(PdfName.BYTERANGE);
            RandomAccessFileOrArray randomAccessFileOrArray = pdfReader.getSafeFile();
            InputStream rg = new RASInputStream(new RandomAccessSourceFactory().createRanged(randomAccessFileOrArray.createSourceView(), pdfArray.asLongArray()));
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            byte[] buf = new byte[128];
            int n = 0;
            while(-1 != (n = rg.read(buf))){
                outputStream.write(buf, 0, n);
            }
            originDate = outputStream.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return originDate;
    }

    //验签
    public static boolean verifyPDF(String pdfOutPath, String certPath, String password){
        boolean verified = false;
        try {
            PdfReader reader = new PdfReader(pdfOutPath); //阅读者
            AcroFields fields = reader.getAcroFields();
            byte [] certData = FileUtil.localBufferRead(certPath); //证书流
            X509Certificate cert = CertUtil.getCertificateByPfx(certData, password);
            KeyStore ks = CertUtil.getKeyStoreByPfx(certData, password);
            //获取签名字段
            List<String> keys = fields.getSignatureNames();
            for (String signedName : keys){
                byte[] sign = PdfSignUtil.getDigitalSignature(reader, signedName);
                byte[] origin = PdfSignUtil.getOriginData(reader, signedName);
                System.out.println(getDigestSignature(pdfOutPath, signedName));
                inspectSignature(fields, signedName);
                PdfPKCS7 pkcs7 = verifySignature(fields, signedName, ks);
                verified = verifyResultSignature(pkcs7);
                verified = CertUtil.PKCS7Verify(cert, origin, sign);
                if (!verified) break;
            }
            reader.close(); //关闭流
        } catch (IOException e) {
            e.printStackTrace();
        }
        return verified;
    }

    //digest signature
    public static String getDigestSignature(String pdfOutPath, String signatureName){
        byte[] stream = new byte[0];
        try {
            PdfReader reader = new PdfReader(pdfOutPath);
            AcroFields fields = reader.getAcroFields();
            AcroFields.Item item = fields.getFieldItem(signatureName);
            PdfDictionary widget = item.getWidget(0);
            PdfDictionary ap = widget.getAsDict(PdfName.AP);
            PdfStream normal = ap.getAsStream(PdfName.N);
            PdfDictionary resource = normal.getAsDict(PdfName.RESOURCES);
            PdfDictionary xobject = resource.getAsDict(PdfName.XOBJECT);
            PdfStream frm = xobject.getAsStream(PdfName.FRM);
            PdfDictionary res = frm.getAsDict(PdfName.RESOURCES);
            PdfDictionary xobj = res.getAsDict(PdfName.XOBJECT);
            PRStream n2 = (PRStream) xobj.getAsStream(PdfName.N2);
            stream = PdfReader.getStreamBytes(n2);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new String(stream);
    }

    //获取pdfpkcs7
    public static PdfPKCS7 verifySignature(AcroFields fields, String signatureName, KeyStore ks){
        PdfPKCS7 pkcs7 = fields.verifySignature(signatureName);
        Certificate[] certs = pkcs7.getCertificates();
        Calendar cal = pkcs7.getSignDate();
        List<VerificationException> errors = CertificateVerification.verifyCertificates(certs, ks, cal);
        if (errors.size() == 0){
            System.out.println("Certificates Verified on KeyStore");
        }else{
            System.out.println(errors);
        }
        return pkcs7;
    }

    public static boolean verifyResultSignature(PdfPKCS7 pkcs7){
        boolean verified = false;
        try {
            verified = pkcs7.verify();
        } catch (GeneralSecurityException e) {
            e.printStackTrace();
        }
        return verified;
    }

    //检查签名的相关信息
    public static void inspectSignature(AcroFields fields, String signatureName) {

        List<AcroFields.FieldPosition> fps = fields.getFieldPositions(signatureName);
        if (fps != null && fps.size() > 0) {
            AcroFields.FieldPosition fp = fps.get(0);
            Rectangle pos = fp.position;
            if (pos.getWidth() == 0 || pos.getHeight() == 0) {
                System.out.println("Invisible Singature");
            }else{
                String format = "Field on page %s; lowerLeftX %s; lowerLeftY %s; upperRightX: %s; upperRightY %s";
                System.out.println(String.format(format, fp.page, pos.getLeft(), pos.getBottom(), pos.getRight(), pos.getTop()));
            }
        }
    }

    //获取签名信息
    public static SignInfo getSignInfo(PdfPKCS7 pkcs7){
        SignInfo signInfo = new PdfSignUtil.SignInfo();
        signInfo.setSignInfo(pkcs7);
        return signInfo;
    }

    //签名信息
    private static class SignInfo{
        private String DigestAlgorithm;
        private String EncryptionAlgorithm;
        private PdfName FilterSubType;
        private String SignatureType;
        private String signer;
        private String SignTime;
        private String location;
        private String reason;

        void setSignInfo(PdfPKCS7 pdfPKCS7) {
            DigestAlgorithm = pdfPKCS7.getDigestAlgorithm();
            EncryptionAlgorithm = pdfPKCS7.getEncryptionAlgorithm();
            FilterSubType = pdfPKCS7.getFilterSubtype();
            X509Certificate cert = pdfPKCS7.getSigningCertificate();
            signer = CertificateInfo.getSubjectFields(cert).getField("CN");
            SimpleDateFormat date_format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SS");
            SignTime = date_format.format(pdfPKCS7.getSignDate());
            location = pdfPKCS7.getLocation();
            reason = pdfPKCS7.getReason();
        }

        public String getDigestAlgorithm() {
            return DigestAlgorithm;
        }

        public String getEncryptionAlgorithm() {
            return EncryptionAlgorithm;
        }

        public PdfName getFilterSubType() {
            return FilterSubType;
        }

        public String getSignatureType() {
            return SignatureType;
        }

        public String getSigner() {
            return signer;
        }

        public String getSignTime() {
            return SignTime;
        }

        public String getLocation() {
            return location;
        }

        public String getReason() {
            return reason;
        }
    }

    public static SignaturePermissions getSignaturePermission(AcroFields fields, String signatureName) {
        PdfDictionary sigDict = fields.getSignatureDictionary(signatureName);
        PdfString contectInfo = sigDict.getAsString(PdfName.CONTACTINFO);
        return new SignaturePermissions(sigDict, null);
    }

}
